package net.util.jport.ports.jdbc;

import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.GnuParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.commons.dbcp.ConnectionFactory;
import org.apache.commons.dbcp.DriverManagerConnectionFactory;
import org.apache.commons.dbcp.PoolableConnectionFactory;
import org.apache.commons.dbcp.PoolingDataSource;
import org.apache.commons.pool.ObjectPool;
import org.apache.commons.pool.impl.GenericObjectPool;

import javax.sql.DataSource;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.Collection;

/**
 * Very simple JDBC jports wrapper, based on DBCP examples.
 * This uses Apache DBCP for managing connections around a single data source.
 */
public class Main {

    private static final Collection<String> helpArgForms =
            Arrays.asList("-h", "-?", "-help", "--help");

    public static void main(String[] args) {

        ConnectionConfig config = null;
        Throwable initError = null;

        /* Build connection config from command line arguments */
        
        Options options = new Options();
        options.addOption("h", "help", false, "help");
        options.addOption("d", "driver", true, "driver class");
        options.addOption("u", "url", true, "connection URL");
        options.addOption("l", "user", true, "username");
        options.addOption("p", "password", true, "password");

        CommandLineParser parser = new GnuParser();

        boolean isHelpRequested = false;

        try {
            org.apache.commons.cli.CommandLine line = parser.parse(options, args);
            if (line.hasOption('h')) {
                isHelpRequested = true;
            } else {
                ConnectionConfig lineConfig = new ConnectionConfig(
                        line.getOptionValue("d"),
                        line.getOptionValue('u'),
                        line.getOptionValue('l'),
                        line.getOptionValue('p'));
                if (lineConfig.driverName == null) {
                    throw new ParseException("driver name is required");
                }
                if (lineConfig.url == null) {
                    throw new ParseException("url is required");
                }

                config = lineConfig;
            }
        } catch (ParseException e) {
            System.err.println("Parsing error: "+e.getMessage());
        }

        if (config == null) {
            usage(options);
            System.exit(isHelpRequested ? 0 : 1);
        }


        /* Initialize driver and datasource */

        DataSource dataSource = null;

        try {
            Class.forName(config.driverName);
            dataSource = setupDataSource(config);
            if (dataSource == null) {
                throw new IllegalStateException("null data source obtained");
            }
        } catch (ClassNotFoundException e) {
            initError = e;
        } catch (Throwable e) {
            initError = e;
        }

        /* Start running */

        // TODO: micro runner etc.

    }

    public static void usage(Options options) {
        HelpFormatter hf = new HelpFormatter();
        PrintWriter printer = new PrintWriter(System.err);
        hf.printHelp(printer,
                78,
                "Usage: java jports-jdbc.jar <options>",
                "",
                options,
                2,
                2,
                "");
        printer.flush();
    }

    public static DataSource setupDataSource(ConnectionConfig config) {
        ObjectPool connectionPool = new GenericObjectPool(null);
        ConnectionFactory connectionFactory =
                new DriverManagerConnectionFactory(config.url, config.username, config.password);
        PoolableConnectionFactory poolableConnectionFactory =
                new PoolableConnectionFactory(connectionFactory,connectionPool,null,null,false,true);
        PoolingDataSource dataSource = new PoolingDataSource(connectionPool);
        return dataSource;
    }

    public static class ConnectionConfig {
        public final String driverName;
        public final String url;
        public final String username;
        public final String password;
        public ConnectionConfig(String driverName, String url, String username, String password) {
            this.driverName = driverName;
            this.url = url;
            this.username = username;
            this.password = password;
        }
    }

}
